﻿using System;
using System.Linq;

namespace Borg
{
    [Serializable]
    internal class InstanceBuilder
    {
        private int _nextExtensionId = 1;
        private int _nextPlugId = 1;
        private int _nextSlotId = 1;
        private int _nextTagId = 1;

        internal Extension CreateExtension(ExtensionType extensionType, Qualifier qualifier)
        {
            var extension = new Extension(NextExtensionId(), extensionType);
            var plugs = new MetaElementCollection<Plug>(extensionType.PlugTypes.Count);

            foreach (var plug in extensionType.PlugTypes.
                Select(plugType => CreatePlug(plugType, extension)))
                plugs.Add(plug);

            extension.Plugs = plugs;
            var slots = new MetaElementCollection<Slot>(extensionType.SlotTypes.Count);

            foreach (var slot in extensionType.SlotTypes.
                Select(slotType => CreateSlot(slotType, extension)))
                slots.Add(slot);

            extension.Slots = slots;
            
            return extension;
        }

        internal Plug CreatePlug(PlugType plugType, Extension extension)
        {
            var plug = new Plug(NextPlugId(), plugType, extension);
            var parameters = new MetaElementCollection<Param>();
            
            foreach (var parameter in plugType.Params)
                parameters.Add(parameter);
            
            var slotDef = plugType.SlotDefinition;

            if (slotDef != null)
                foreach (var paramDef in slotDef.ParamDefinitions.
                    Where(paramDef => paramDef.DefaultParam != null &&
                                      !parameters.Contains(paramDef.Name)))
                    parameters.Add(paramDef.DefaultParam);

            plug.Params = parameters;
            
            return plug;
        }

        internal Slot CreateSlot(SlotType slotType, Extension extension)
        {
            return new Slot(NextSlotId(), slotType, extension);
        }

        internal Tag CreateTag(string name, Slot slot, Plug plug)
        {
            return new Tag(NextTagId(), name, slot, plug);
        }

        internal int NextExtensionId()
        {
            return _nextExtensionId++;
        }

        internal int NextPlugId()
        {
            return _nextPlugId++;
        }

        internal int NextSlotId()
        {
            return _nextSlotId++;
        }

        internal int NextTagId()
        {
            return _nextTagId++;
        }
    }
}

